<?php
/**
*
*
*
*/
use Yz_Log_Log as Log;

class Error
    extends Exception
{

    /** subclasses SHOULD define own messages
     *  @var string
     */
    const ERROR_MESSAGE = 'General error';

    /** subclasses MAY redefine
     *
     */
    const ERROR_CODE = Log::CRITICAL;

    /**
     *  @var array
     */
    protected $values = array();

    protected $realTrace;

    /****************************
     *      Public methods      *
     ****************************/

    /**
     *  static shortcut
     *      the values are taken from
     *  @param  array args
     *  @param  Exception prev
     *  @return Error
     */
    public static function _new_(array $args=null, Exception $prev=null)
    {
        $msg = static::ERROR_MESSAGE;
        $code = static::ERROR_CODE;
        Log::log($msg, $code/* debug */);
        $ex = new static($msg, $code, $prev);
#        $tr = array_slice(debug_backtrace(), 1);
        //array_shift($tr); // remove current method call from trace
        if ($args)
            $ex->values = $args;
        $tr = debug_backtrace();
        $currentTrace = $tr[0]; // add the last trace entry as values
        // objects or classes that threw the error are in the previous entry
        $ex->values += $currentTrace + $tr[1];
        $ex->values['trace'] = $tr;

        // insert values into message
        $ex->message = Yz_String::replaceVars( $ex->message, $ex->values );

        // now it's safe to Log
        Log::log( "$ex", $code );
        Log::log( $ex->getNormalTrace(), Log::DEBUG );
        return $ex;
    }


    /**
    *
    */
    public function __toString()
    {
        try {
            $o = $this->toString();
        } catch (Exception $ex) {
            debug("toString Exception: ".$ex->getTraceAsString());
            $o = get_class($this)
                . " at "
                . $this->getFile()
                . "@"
                . $this->getLine()
                . ": "
                . $this->getMessage();
        }
        return $o;
    }

    public function __get($var)
    {
        return isset($this->values[$var]) ? $this->values[$var] : null;
    }

    public function __set($var, $value)
    {
        isset($this->values[$var])
            and $this->values[$var] = $value;
        return $this;
    }

    /**
    *
    *
    */
    public function toString()
    {
        $file = isset($this->values['file']) ? $this->values['file'] : $this->file;
        $line = isset($this->values['line']) ? $this->values['line'] : $this->line;
        $str = get_class( $this ) . " at {$file}@{$line}: {$this->message}";
        return $str;
    }

    /**
    *
    *
    */
    public function getNormalTrace()
    {
        if (! isset($this->values['trace']))
            return $this->getTraceAsString();
        $str = "";
        foreach ($this->values['trace'] as $depth => $ent) {
            $str .= str_pad("#{$depth}: ", 6);
            if (isset($ent['file'])) {
                $str .= $ent['file'];
                if (isset($ent['line'])) {
                    $str .= '@' . $ent['line'];
                }
                $str .= ":\n      ";
            }
            if (isset($ent['object'])) {
                $str .= "{$ent['class']}({$ent['object']}):\n      ";
            } else if (isset($ent['class'])) {
                $str .= "{$ent['class']}:\n      ";
            }
            if (isset($ent['function'])) {
                $str .= (is_string($ent['function']) ? $ent['function'] : "<function>")
                    . '('
                    . (isset($ent['args'])
                            ? join(', ', array_map(function($v) {
                                    if(null === $v) return 'NULL';
                                    if(true === $v) return 'TRUE';
                                    if(false === $v) return 'FALSE';
                                    if(is_string($v)) return "'$v'";
                                    if(is_scalar($v)) return $v;
                                    if(is_array($v)) return 'ARRAY';
                                    if($v instanceof Yz_Class) return "$v";
                                    if(is_object($v)) return get_class($v);
                                    return gettype($v);
                                },
                                $ent['args']))
                            : "")
                    . ')'
                ;
            }
            $str .= "\n";
        }
        return $str;
    }


}

